using System;
using System.Collections.Generic;
using System.Numerics;
using TTEngine.Graphics;
using TTEngine.TTMath;
using Silk.NET.OpenGLES;
using TTEngine.Resource;
using voidbox;

namespace TTEngine.Graphics
{
    public class Tile
    {
        public int index;
        public int posU;
        public int posV;
    }

    public class TileLayer
    {
        public Tile smallpot;  // 小点点
        public Tile bigpot;    // 大点点
        public Tile[] innerCorner = new Tile[4];  // 左上 右上 左下 右下
        public Tile[] outCorner = new Tile[9];    // 左上角开始，扫描 9个，中间那个outCorner[4]是tiled
        public Tile[] tiled = new Tile[4];        // 替换的Tiled 三个
    }

    public class TiledTex : ResourceObj
    {
        private uint _width;
        private uint _height;
        private uint _tileSize;
        private List<Tile> _tiles = new List<Tile>();
        private List<TileLayer> _tileLayers = new List<TileLayer>();
        private Material _tiledMaterial;
        private Tile _tileEmpty;
        private ImageData _pixelBuf;
        private Texture2D _texture;
        private bool _dirty;

        public uint Width => _width;
        public uint Height => _height;
        public uint TileSize => _tileSize;
        public Texture2D Texture => _texture;

        public TiledTex(ResourcePackage package, uint width, uint height, uint tileSize, TextureType format)
            : base(package)
        {
            _width = width;
            _height = height;
            _tileSize = tileSize;
            _dirty = false;
            _pixelBuf = new ImageData((int)width, (int)height, (format == TextureType.RGBA_32) ? ImageFormat.RGBA : ImageFormat.Gray);
            _texture = new Texture2D(package, width, height, format);
        }

        protected override void OnDestory()
        {
            _texture.DecRef();
            if (_texture.CanDispose)
            {
                _texture.Destory();
            }
        }

        public Tile GetEmpty()
        {
            return _tileEmpty;
        }

        public Tile AddEmpty()
        {
            if (_tileEmpty != null)
                return _tileEmpty;

            var t = new Tile
            {
                index = 0,
                posU = 0,
                posV = 0
            };
            _tiles.Add(t);
            _tileEmpty = t;
            return t;
        }

        public Tile AddTile(ImageData data)
        {
            return SetTile(_tiles.Count, data);
        }

        public Tile SetTile(int index, ImageData data)
        {
            if (data.Width != _tileSize || data.Height != _tileSize)
                throw new Exception($"错误的TileSize:{data.Width},{data.Height}");

            int tileMaxCol = (int)(_width / _tileSize);
            int ux = index % tileMaxCol;
            int uy = index / tileMaxCol;
            int tileMaxRow = (int)(_height / _tileSize);

            if (uy < 0 || uy >= tileMaxRow)
                throw new Exception("index 超出范围");

            while (_tiles.Count <= index)
                _tiles.Add(null);

            var t = new Tile
            {
                posU = ux,
                posV = uy,
                index = index
            };
            _tiles[index] = t;

            if (data.Type != ImageFormat.RGBA)
            {
                var rgbaData = new ImageData(data.Width, data.Height, ImageFormat.RGBA);
                for (int y = 0; y < data.Height; y++)
                {
                    for (int x = 0; x < data.Width; x++)
                    {
                        var gray = data.GetGray(x, y);
                        var color = new Color32 { r = gray, g = gray, b = gray, a = 255 };
                        rgbaData.SetRGBA(x, y, color);
                    }
                }
                data = new ImageData(rgbaData.Width, rgbaData.Height, rgbaData.Type);
                data.SetData(rgbaData.GetData());
            }

            for (int y = 0; y < data.Height; y++)
            {
                for (int x = 0; x < data.Width; x++)
                {
                    var color = data.GetRGBA(x, y);
                    _pixelBuf.SetRGBA(t.posU * (int)_tileSize + x, t.posV * (int)_tileSize + y, color);
                }
            }
            _dirty = true;

            return t;
        }

        public TileLayer AddLPCTile(IImage data, Vector2 start = default)
        {
            var tl = new TileLayer();
            _tileLayers.Add(tl);

            tl.smallpot = AddTile(CutSprite(data, 0 * (int)_tileSize + (int)start.X, 1 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.bigpot = AddTile(CutSprite(data, 0 * (int)_tileSize + (int)start.X, 0 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));

            tl.innerCorner[0] = AddTile(CutSprite(data, 1 * (int)_tileSize + (int)start.X, 0 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.innerCorner[1] = AddTile(CutSprite(data, 2 * (int)_tileSize + (int)start.X, 0 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.innerCorner[2] = AddTile(CutSprite(data, 1 * (int)_tileSize + (int)start.X, 1 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.innerCorner[3] = AddTile(CutSprite(data, 2 * (int)_tileSize + (int)start.X, 1 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));

            tl.outCorner[0] = AddTile(CutSprite(data, 0 * (int)_tileSize + (int)start.X, 2 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.outCorner[1] = AddTile(CutSprite(data, 1 * (int)_tileSize + (int)start.X, 2 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.outCorner[2] = AddTile(CutSprite(data, 2 * (int)_tileSize + (int)start.X, 2 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));

            tl.outCorner[3] = AddTile(CutSprite(data, 0 * (int)_tileSize + (int)start.X, 3 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.outCorner[4] = AddTile(CutSprite(data, 1 * (int)_tileSize + (int)start.X, 3 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.outCorner[5] = AddTile(CutSprite(data, 2 * (int)_tileSize + (int)start.X, 3 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));

            tl.outCorner[6] = AddTile(CutSprite(data, 0 * (int)_tileSize + (int)start.X, 4 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.outCorner[7] = AddTile(CutSprite(data, 1 * (int)_tileSize + (int)start.X, 4 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.outCorner[8] = AddTile(CutSprite(data, 2 * (int)_tileSize + (int)start.X, 4 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));

            tl.tiled[0] = tl.outCorner[4];
            tl.tiled[1] = AddTile(CutSprite(data, 0 * (int)_tileSize + (int)start.X, 5 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.tiled[2] = AddTile(CutSprite(data, 1 * (int)_tileSize + (int)start.X, 5 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));
            tl.tiled[3] = AddTile(CutSprite(data, 2 * (int)_tileSize + (int)start.X, 5 * (int)_tileSize + (int)start.Y, (int)_tileSize, (int)_tileSize));

            return tl;
        }

        private ImageData CutSprite(IImage source, int x, int y, int width, int height)
        {
            var result = new ImageData(width, height, source.Type);
            var resultData = new byte[width * height * (source.Type == ImageFormat.RGBA ? 4 : 1)];

            for (int j = 0; j < height; j++)
            {
                for (int i = 0; i < width; i++)
                {
                    if (source.Type == ImageFormat.RGBA)
                    {
                        var color = source.GetRGBA(x + i, y + j);
                        var index = (j * width + i) * 4;
                        resultData[index] = color.r;
                        resultData[index + 1] = color.g;
                        resultData[index + 2] = color.b;
                        resultData[index + 3] = color.a;
                    }
                    else
                    {
                        var gray = source.GetGray(x + i, y + j);
                        resultData[j * width + i] = gray;
                    }
                }
            }

            result.SetData(resultData);
            return result;
        }

        public void Apply(bool force = false)
        {
            if (_dirty || force)
            {
                _texture.UploadImageData(0, 0, _width, _height, _pixelBuf);
                _dirty = false;
            }
        }

        public List<TileLayer> GetTileLayers() => _tileLayers;
        public int GetTileLayerCount() => _tileLayers.Count;
        public TileLayer GetTileLayer(int index) => _tileLayers[index];
        public int GetTileCount() => _tiles.Count;
        public Tile GetTile(int index) => _tiles[index];
    }
}